/*! \file guiclass.h	
	
	This contains the implementation of the GUI class
*/

#include "guiclass.h"

GUI* GUI::gui = 0;
Font* GUI::font = 0;
int GUI::nextGuiId = 0;

bool GUI::rShiftHeld = false;
bool GUI::lShiftHeld = false;
bool GUI::rCtrlHeld = false;
bool GUI::lCtrlHeld = false;
bool GUI::lMouseHeld = false;

// private
GUI::GUI() : 
	Component( 0 ),
	lockedComponent( 0 )
{
	// This is so that children components don't move anywhere since components
	// base where to draw according to their parent
	setPosition( 0, 0 );
	font = new Font( FONT_DIRECTORY"corbel.ttf", 16 );
	focusedComponent = 0;
}

GUI* GUI::getInstance(){
	if ( gui == 0 ) {
		gui = new GUI();
	}
	return gui;
}

void GUI::update() {
	if ( !allContainers.empty() ) {
		list<Component*>::iterator i;
		for ( i = allContainers.begin(); i != allContainers.end(); ++i ) {
			(*i)->update();
		}
	}
}

// Drawing done in reverse order
void GUI::draw() {
	if ( !allContainers.empty() ) {
		
		(*allContainers.begin())->setGUIStyle( 10, 11, 8, 9 );
		list<Component*>::reverse_iterator i;
		for ( i = allContainers.rbegin(); i != allContainers.rend(); ++i ) {
			glPushMatrix();
			glTranslatef( (float)(*i)->getX(), (float)(*i)->getY(), 0.0f );
			(*i)->draw();
			glPopMatrix();
		}
		
		(*allContainers.begin())->setGUIStyle( 6, 7, 8, 9 );
	}
	HoverText::getInstance()->draw();
}


void GUI::add( Component* c ) {
	cout << "Added a component to the GUI" << c->getID() << endl;
	
	allContainers.push_back( c );
}

bool GUI::handleMouseMove( int mX, int mY ) { 
	// If a component is locked (like dragging the scrollbar in a scrollpanel) then
	// it will be the only thing to accept mouseMove
	if ( lockedComponent != 0 ) {
		// If the left mouse button is still held, then this shouldn't remove the lock, because that means it could
		// be held by a scrollbar that is being dragged.
		// Also, this needs to be the container with the lock for it to be able to reset the lock
	
		if ( !lockedComponent->handleMouseMove( mX, mY ) && !lMouseHeld/*order matters*/ ) {
			GUI::getInstance()->setLockedComponent( 0 );
		}
		if ( !lMouseHeld ) {
			list<Component*>::iterator i;
			for ( i = allContainers.begin(); i != allContainers.end(); ++i ) {
				// Check only components above the locked one
				if ( (*i) == lockedComponent ) {
					break;
				}
				// If the mouse is over a component above the locked one, then that one releases the lock
				int x = (*i)->getX();
				int y = (*i)->getY();
				int w = (*i)->getWidth();
				int h = (*i)->getHeight();
				if ( mX >= x && mX <= x + w && mY >= y && mY <= y + h ) {
					//~ cout << "GUI IS UNLOCKING" << endl;
					//~ cout << "mx " << mX << " mY " << mY << "x " << x << " y " << y << " w " << w << " h " << h << endl;
					GUI::getInstance()->setLockedComponent( 0 );
					break;
				}
			}	
		}
		
	} else {
		list<Component*>::iterator i;
		for ( i = allContainers.begin(); i != allContainers.end(); ++i ) {
			if ( (*i)->handleMouseMove( mX, mY ) ) {
				return true;
			}
		}	
	}
	return false;
}

bool GUI::handleMouseWheel( int pos ) {
	// Mouse wheel just goes to focused opponent
	if ( focusedComponent ) {
		focusedComponent->handleMouseWheel( pos );
		return true;
	}
	return false;
	//~ list<Component*>::iterator i;
	//~ for ( i = allContainers.begin(); i != allContainers.end(); ++i ) {
		//~ (*i)->handleMouseWheel( pos );
	//~ }	
}		
	
bool GUI::handleMouseClick( int button, int state ) { 
	lMouseHeld = ( state == GLFW_PRESS );
	
	list<Component*>::iterator i;
	for ( i = allContainers.begin(); i != allContainers.end(); ++i ) {
		// Handle the click on the first container to answer the click
		if ( (*i)->handleMouseClick( button, state ) ) {
			return true;
		}
	}	
	return false;
}
void GUI::handleKeyInput( int key, int state ) { 
	if ( focusedComponent ) {
		focusedComponent->handleKeyInput( key, state );
	}
	//~ list<Component*>::iterator i;
	//~ for ( i = allContainers.begin(); i != allContainers.end(); ++i ) {
		//~ (*i)->handleKeyInput( key, state );
	//~ }	
}

void GUI::drawRectangle( int x, int y, int w, int h, float r1, float g1, float b1, float r2, float g2, float b2 ) {
	glDisable( GL_TEXTURE_2D );
	glColor3f( r1, g1, b1 );
	glPushMatrix();
	glLoadIdentity();
	glTranslatef( x, y, 0 );
	
	glBegin(GL_QUADS);
		glVertex2i( 0, 0 );
		glVertex2i( w, 0 );
		glColor3f( r2, g2, b2 );
		glVertex2i( w, h );
		glVertex2i( 0, h );
	glEnd();
	
	glPopMatrix();
}

void GUI::drawRectangle( int x, int y, int w, int h, Color& lowColor, Color& highColor, bool vertical ) {
	glPushMatrix();
	glDisable( GL_TEXTURE_2D );
	glColor4fv( lowColor.array );
	glLoadIdentity();
	glTranslatef( x, y, 0 );
	glBegin(GL_QUADS);
		if ( vertical ) {
			glVertex2i( 0, 0 );
			glVertex2i( w, 0 );
			glColor4fv( highColor.array );
			glVertex2i( w, h );
			glVertex2i( 0, h );
		} else {
			glVertex2i( 0, h );
			glVertex2i( 0, 0 );
			glColor4fv( highColor.array );
			glVertex2i( w, 0 );
			glVertex2i( w, h );
		}
	glEnd();
	
	glPopMatrix();
}

void GUI::drawRectangle( int w, int h, Color& lowColor, Color& highColor, bool vertical ) {
	glDisable( GL_TEXTURE_2D );
	glColor4fv( lowColor.array );
	glBegin(GL_QUADS);
		if ( vertical ) {
			glVertex2i( 0, 0 );
			glVertex2i( w, 0 );
			glColor4fv( highColor.array );
			glVertex2i( w, h );
			glVertex2i( 0, h );
		} else {
			glVertex2i( 0, h );
			glVertex2i( 0, 0 );
			glColor4fv( highColor.array );
			glVertex2i( w, 0 );
			glVertex2i( w, h );
		}
	glEnd();
}

void GUI::setClippingArea( int x, int y, int w, int h ) {
	glClear(GL_STENCIL_BUFFER_BIT);
	
	glPushMatrix();
	glLoadIdentity();
	// create a stencil
	glEnable(GL_STENCIL_TEST);
	glColorMask(0,0,0,0); // disable drawing
	glStencilFunc(GL_ALWAYS, 1, 1);
	glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
	
	glBegin(GL_QUADS); // draw stencil
		glVertex2d( x,y );
		glVertex2d( x + w,y );
		glVertex2d( x + w, y + h );
		glVertex2d( x, y + h );
	glEnd();
	
	glColorMask(1,1,1,1);
	glStencilFunc(GL_EQUAL, 1, 1);
	glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
	
	glPopMatrix();
}
	
void GUI::setFocusedComponent( Component* c ) {
	focusedComponent = c;
	// Since this is a container, it is probably inside this GUI's container list,
	// so check for it. If it is, move it to the front of the list so that it draws first.
	bool found = false;
	list<Component*>::iterator i;
	for ( i = allContainers.begin(); i != allContainers.end(); ++i ) {
		if ( *i == c ) {
			allContainers.erase( i );
			found = true;
			break;
		}
	}
	if ( found ) {
		allContainers.push_front( c );
	}
}

void GUI::setLockedComponent( Container* c ) {

	// You can't lock a component if it isn't a top-level container (parent is GUI)
	// Also, if the component you're trying to lock is already locked, then return immediately
	if ( c != 0 && ( this != c->getParent() || lockedComponent == c ) ) {
		return;
	}
	
	// if you're setting a new locked component
	// then alert the old one of a mouseUp event
	if ( lockedComponent != 0 ) {
		lockedComponent->sendOnLeaveEvent();
		lockedComponent->sendMouseUpEvent();
	}

	// If it is null, then we need to do a new "handleMouseMove" event in case of this scenario:
	// 1. You drag a scroll bar around from outside of its grandparent container.
	// 2. You let go of the mouse button while you're over a button.
	// 3. Now, you'd want to spawn the hover-text of that button, but you can't without a mouseMove event
	if ( c == 0 ) {
		//~ cout << "Setting locked component to zero" << endl;
		lockedComponent = 0;
		
		int mX, mY;
		glfwGetMousePos( &mX, &mY );
		handleMouseMove( mX, mY );
		return;
	}
	
	// Since this is a container, it is probably inside this GUI's container list,
	// so check for it. If it is, then it can be locked
	bool found = false;
	list<Component*>::iterator i;
	for ( i = allContainers.begin(); i != allContainers.end(); ++i ) {
		if ( *i == c ) {
			found = true;
			break;
		}
	}
	if ( found ) {
		cout << "Giving a component the lock" << endl;
		lockedComponent = c;
	} else {
		lockedComponent = 0;
	}
}

void GUI::removeClippingArea() {
	glDisable(GL_STENCIL_TEST);
}

int GUI::nextAvailableID() {
	return nextGuiId++;
}

bool GUI::shiftHeld() {
	return rShiftHeld || lShiftHeld;
}
	
bool GUI::ctrlHeld() {
	return rCtrlHeld || lCtrlHeld;
}

Component* GUI::getFocusedComponent() {
	return focusedComponent;
}

int GUI::getType() {
	return TYPE_GUI;
}

Container* GUI::getLockedComponent() {
	return lockedComponent;
}
